window.domain = "streamwork.com";

var init = function (itemId, initialData, activityOptions) {
	var $ = jQuery, instanceId, predefinedQuestionnaires, isLocked, lang,
		/* public methods (called by streamwork) */
		handlers, render, external_update, setQuestionnaire,
		/* private methods */
		id, renderQuestions, renderSections, renderTabs, onChangeHandler, responseItemRenderer, createDateSelector, createTable;

	instanceId = new Date().getTime();

	predefinedQuestionnaires = {};

	// set defaults
	initialData = initialData || {};
	initialData.answers = initialData.answers || {};
	initialData.questionnaire = initialData.questionnaire || {};

	lang = $.isArray(initialData.languages) && initialData.length > 0 ? initialData.languages[0] : "en"; // the display language is determined by the first entry in initialData.languages
	//langFallback = initialData.languages[1] || "en";												// ... the fallback language can be specified by the second entry - or defeults to EN

	// this one is called when the user changes data
	onChangeHandler = function (questionId, newValue) {
		var oldValue;
		oldValue = initialData.answers[questionId];
		initialData.answers[questionId] = newValue;													// todo: should we postpone this? (... wait for the confirmation first - see external_update below)
		$('#' + id('activityIndicator')).removeClass('done').addClass('active').attr('title', 'Updating questionnaire data...');
		osapi.appdata.update({userId: "@shared",data: {initdata: initialData}}).execute(function (userData) {
			if (userData.error){
				console.log(userData.error.message)
			}
			else{				      
				$('#' + id('activityIndicator')).removeClass('active').addClass('done')				//    ... stop the activity indicator
					.attr('title', 'The questionnaire data was stored successfully');
			}
		});
		
	};

	// this one is called when data was changed from outside (e.g. other SW user, API)
	external_update = function (data) {
		var questionId;
		if (data.message.sender === instanceId) {													// was this one of the user's own updates?
																									// todo: this seems not to prevent anything -> streamwork is aware of this and plans to adress this within the next releases!
			if (data.message.newValue !== data.value) {												// check if streamwork successfully received the data (maybe not if simultaneously updated by other users?)
				alert('Data could not be saved. Please try again!');								// alert the user and revert to the original value (see below)
			} else {																				// if not, everything seems to be fine.
				$('#' + id('activityIndicator')).removeClass('active').addClass('done')				//    ... stop the activity indicator
					.attr('title', 'The questionnaire data was stored successfully');
				return;																				//    ... and prevent further recursions
			}
		}
		questionId = data.path[1];
		$('#' + id('var', questionId)).trigger('externalupdate', [{
			newValue: data.value
		}]);
		initialData.answers[questionId] = data.value;												// todo: should we postpone this? (... wait for the confirmation first - see external_update below)
	};


	responseItemRenderer = {																		// todo: do this in a more object oriented way and inherit
		'date': function (question, subQuestions) {
			var el, subQuestion, i, tableData = [], createInput, input;
			createInput = function (subQuestion) {													// todo: reuse the "createInput" from 'short-text'?
				var input;
				input = $('<input type="text">');													// IE Bug, see http://api.jquery.com/jQuery/#jQuery2
				input.attr({
					'id':		id('var', subQuestion.questionId),
					'value':	initialData.answers[subQuestion.questionId] || ""
				});
				input.change(function () {
					onChangeHandler(subQuestion.questionId, $(this).val());
				});
				input.bind('externalupdate', function (e, data) {
					$(this).val(data.newValue);
				});
				if (isLocked) {
					input.attr({'readonly': 'readonly', 'disabled': 'disabled'});
				}
				input.datepicker({ dateFormat: "yy-mm-dd",showButtonPanel: true });
				return input;
			};
			if (subQuestions.length === 1) {														// create a single text-input
				input = createInput(subQuestions[0]);
				tableData[0] = [
					input
					//,createDateSelector(input.attr('id'))
				];
			} else {
				for (i = 0; (subQuestion = subQuestions[i]); i++) {									// create a table for multiple subquestions
					input = createInput(subQuestion);
					tableData[i] = [
						subQuestion.text[lang] + ':',
						input
						//,createDateSelector(input.attr('id'))
					];
				}

			}
			el = createTable(tableData);
			return el;
		},
		'short-text': function (question, subQuestions) {
			var el, subQuestion, i, tableData = [], createInput;
			createInput = function (subQuestion) {
				var input;
				if (question.options && question.options.size === "long") {
					input = $('<textarea>');
				} else {
					input = $('<input type="text">');												// IE Bug, see http://api.jquery.com/jQuery/#jQuery2
				}
				input.attr({
					'id':		id('var', subQuestion.questionId),
					'value':	initialData.answers[subQuestion.questionId] || ""
				});
				input.change(function () {
					onChangeHandler(subQuestion.questionId, $(this).val());
				});
				input.bind('externalupdate', function (e, data) {
					$(this).val(data.newValue);
				});
				if (isLocked) {
					input.attr({'readonly': 'readonly', 'disabled': 'disabled'});
				}
				return input;
			};
			if (subQuestions.length === 1) {														// create a single text-input
				el = createInput(subQuestions[0]);
			} else {
				for (i = 0; (subQuestion = subQuestions[i]); i++) {									// create a table for multiple subquestions
					tableData[i] = [
						subQuestion.text[lang] + ':',
						createInput(subQuestion)
					];
				}
				el = createTable(tableData);
			}
			return el;
		},
		'number': function (question, subQuestions) {
			question.options = {'validator': 'todo'};
			return responseItemRenderer['short-text'](question, subQuestions);						// todo extend question.options
		},
		'long-text': function (question, subQuestions) {
			question.options = {'size': 'long'};
			return responseItemRenderer['short-text'](question, subQuestions);						// todo extend question.options
		},
		'select': function (question, subQuestions) {
			var el, subQuestion, i, tableData = [], createOption, createOptions;
			createOption = function (option, questionId, index) {
				var input, label;
				input = $('<input type="radio" name="' + id('var', questionId) + '">');				// IE Bug, see http://api.jquery.com/jQuery/#jQuery2
				input.attr({
					'id':		(index > 0 ? id('var', questionId, index) : id('var', questionId)),
					//'name':		id('var', questionId),											// bug in ie9 -> moved to the element creation call, see above!
					'value':	option.value
				});
				if (initialData.answers[questionId] !== undefined) {								// due to a bug in ie8(or jQ) we need to check for undefined first.
					input.val([initialData.answers[questionId]]);
				}
				input.change(function () {
					onChangeHandler(questionId, $(this).val());
				});
				if (index === 0) {																	// We only need one event handler for the whole group
					input.bind('externalupdate', function (e, data) {
						var group = $(this).attr('name');
						$('input[name=' + group + ']').val([data.newValue]);
					});
				}
				if (isLocked) {
					input.attr({'readonly': 'readonly', 'disabled': 'disabled'});
				}
				label = $('<label>');
				label.attr('for', input.attr('id'));
				label.html(option.text[lang]);
				return [input, label];
			};
			createOptions = function (options, questionId) {
				var result = [], i, option;
				for (i = 0; (option = options[i]); i++) {
					result = $.merge(result, createOption(option, questionId, i));
				}
				return result;
			};
			if (subQuestions.length === 1) {														// create a single text-input
				tableData[0] = $.merge(
					subQuestions[0].text ? [subQuestions[0].text[lang] + ':'] : [],
					createOptions(question.options, subQuestions[0].questionId)
				);
			} else {
				for (i = 0; (subQuestion = subQuestions[i]); i++) {									// create a table for multiple subquestions
					tableData[i] = $.merge(
						[subQuestion.text[lang] + ':'],
						createOptions(question.options, subQuestion.questionId)
					);
				}
			}
			return createTable(tableData);
		},
		'boolean': function (question, subQuestions) {
			var selects, conditionalWrapper = null, conditional, yesOption, noOption, visible, label;
			question.options = question.options || [												// use the 'select'-renderer with the provided texts...
				{value: "yes", text: {"en": "yes"}},												// ... or use the default answer options for questionnaireVersion<3
				{value: "no",  text: {"en": "no"}}
			];
			selects = responseItemRenderer.select(question, subQuestions);
			// special case -> additional "conditional" answer
			if (subQuestions.length === 1 && question.conditional) {								// we only support conditionals for single-option questions
				conditionalWrapper = $('<div>').addClass('responses conditional single ' +
														 question.conditional.type);
				conditional = responseItemRenderer[question.conditional.type](
									question.conditional,											//
									[{ questionId: question.conditional.questionId }]				// ... and only support single-option conditionals!
							  );
				// set the initial visibility
				yesOption = $('#' + id('var', question.questionId), selects);
				noOption  = $('#' + id('var', question.questionId, '1'), selects);
				visible = (question.conditional.condition === true  && yesOption.attr('checked') === "checked") ||
						  (question.conditional.condition === false && noOption.attr('checked') === "checked");
				conditionalWrapper.css('display', visible ? 'block' : 'none');
				// add _additional_ change handlers to hide/show the text field!
				yesOption.change(function () {
					var visible = question.conditional.condition === ($(this).attr('checked') === "checked");
					conditionalWrapper[visible ? 'slideDown' : 'slideUp']('fast',function(){gadgets.window.adjustHeight();});
				});
				noOption.change(function () {
					var visible = question.conditional.condition === ($(this).attr('checked') !== "checked");
					conditionalWrapper[visible ? 'slideDown' : 'slideUp']('fast',function(){gadgets.window.adjustHeight();});
				});
				yesOption.bind('externalupdate', function (e, data) {
					var visible = question.conditional.condition === ($(this).attr('checked') === "checked"); // same as yesOption.change!
					conditionalWrapper[visible ? 'slideDown' : 'slideUp']('fast',function(){gadgets.window.adjustHeight();});
				});
				//label = $("<label>").attr('for', conditional.attr('id')).html(question.conditional.title[lang] + ':');
				conditionalWrapper.append(label, conditional);
			}
			return $('<div>').append(selects, conditionalWrapper);
		},
		'priority': function (question, subQuestions) {
			var el, subQuestion, i, tableData = [], createSlider;
			createSlider = function (options, questionId) {
				var slider, label;
				slider = $('<div>').attr('id', id('var', questionId));
				slider.css('width', (options.length * 40) + 'px');
				slider.slider({
					value:	initialData.answers[questionId] || 0,
					min:	0,
					max:	options.length - 1,
					step:	1,
					slide:	function (event, ui) {
						label.html(question.options[ui.value].text[lang]);
					}
				});
				label = $('<label>').html(question.options[initialData.answers[questionId] || 0].text[lang]);
				slider.bind('slidechange', function (e, ui) {
					if (e.originalEvent === undefined) {											// ignore external_updates (via api call, see below)
						return;
					}
					onChangeHandler(questionId, $(this).slider("value"));
				});
				slider.bind('externalupdate', function (e, data) {
					$(this).slider("value", data.newValue);
					label.html(question.options[data.newValue].text[lang]);							// todo: add this to the test cases
				});
				if (isLocked) {
					slider.slider('disable');
				}
				return [slider, label];
			};
			if (subQuestions.length === 1) {														// create a single text-input
				el = createTable([
						createSlider(question.options, subQuestions[0].questionId)
					]);
			} else {
				for (i = 0; (subQuestion = subQuestions[i]); i++) {									// create a table for multiple subquestions
					tableData[i] = $.merge(
						[subQuestion.text[lang] + ':'],
						createSlider(question.options, subQuestion.questionId)
					);
				}
				el = createTable(tableData);
			}
			return el;
		},
		'participants': function (question, subQuestions) {											// special control which lists all participants
			var participants, participant, getAddtionalData, tableData = [], i = 0,					// and is able to store a role description for each one
				image, name, contact, role, labels;
			labels = question.labels || {
				role: {en: "Role"},
				name: {en: "Name"},
				contact: {en: "Contact"},
				phone: {en: "Phone"}
			}
			//participants = streamwork.getParticipants();											// todo: check if "streamwork" is defined?
			osapi.people.getViewerFriends().execute(function(result){
			    if (result.error){
			      document.getElementById("content").innerHTML = result.error.message;
			    }
			    else {			      
			      for (var n in result){
			      		var participants=result[n];
			      
				      	for (; (participant = participants[i]); i++) {
							image = $("<img>").attr({src: participant.thumbnailUrl});
							name = $("<div>")
										.append("<h3>" + labels.name[lang] + "</h3>").append(participant.displayName);
							contact = $("<div>")
										.append("<h3>" + labels.contact[lang] + "</h3>")
										.append($("<div>").attr('id', id('participant', participant.id)));
							role = $("<div>").append("<h3>" + labels.role[lang] + "</h3>").append(
								responseItemRenderer['short-text']({}, [{
									questionId: subQuestions[0].questionId + '-' + participant.id
								}])
							);
							tableData.push([
								image,
								name,
								contact,
								role
							]);
							getAddtionalData(participant.id);
						}
					}
					$(".responses").html(createTable(tableData));
					gadgets.window.adjustHeight();
			    }
			});
			getAddtionalData = function (participantId) {
				window.domain = "streamwork.com";
				$.get("/users/user_profile_as_xml/" + participantId, function (data) {				// fill in further data asynchronously
					var profile = $(data).children('profile');
					$('#' + id('participant', participantId))
						.append($("<a>").attr('href', 'mailto:' + profile.children('email_address').text()).html(profile.children('email_address').text()))
						.append("<br/>")
						.append($("<span>").html(labels.phone[lang] + ': ' + profile.children('office_phone').text()));
				});
			};			
			return createTable(tableData);
		},
		'void': function (question, subQuestions) {
			return null;
		}
	};

	render = function () {
		var area, el, itemNo;
		itemNo = itemId.substr(itemId.lastIndexOf('_') + 1);										// check if the item is locked. this is just a quick work around since there is
		isLocked = $('div[item_id=' + itemNo + ']').hasClass('item_locked') || streamwork.isActivityReadOnly();													// ... or if the whole activity is locked!
		area = $('#'+itemId)
			.empty()																				// clear the complete innerHtml
			.addClass('rdsbcQuestionnaire')
			.append($('<div>').attr({
				'id':		id('activityIndicator'),
				'class':	'activityIndicator'
			}));
		if (initialData.questionnaire.length === 1) {												// just skip the tabs there is only one section
			el = renderSections(initialData.questionnaire[0]);
		} else {
			el = renderTabs(initialData.questionnaire);
		}
		area.append(el);
		gadgets.window.adjustHeight();
	};

	renderTabs = function (questionnaire) {
		var i = 0, el, group, header, tabId, tabsClass, toggleTabs, tab;
		el = $('<div>').addClass("tabs");
		header = $('<div>').addClass('tabsHeader').attr('id', id('tabsHeader'));
		tabsClass = id('tabs');
		toggleTabs = function (e) {
			e.preventDefault();
			$('#' + id('tabsHeader') + ' a').removeClass('active');
			$(this).addClass('active');
			$('.' + tabsClass).hide();																// todo: optimize? remember the tab ids?...
			$('#' + e.data.tabId).show();				
			gadgets.window.adjustHeight();
		};
		for (; (group = questionnaire[i]); i++) {
			tabId = id('tab', i);
			header.append(
				$('<a>').attr({'href': '#'})
					.html(group.sectionName[lang])
					.click({tabId: tabId}, toggleTabs)
					.addClass(i === 0 ? 'active' : '')
				);
			tab = renderSections(group).addClass('tab').addClass(tabsClass).attr('id', tabId);
			if (i !== 0) {																			// we just show the first tab!
				tab.hide();
			}
			el.append(tab);
		}
		el.prepend(header);		
		return el;
	};

	renderSections = function (sections) {
		var i, section, div;
		div = $('<div>')
			.addClass('sections');
		for (i = 0; (section = sections.sections[i]); i++) {
			if (section.sectionName && section.sectionName[lang]) {
				div.append('<h3>' + section.sectionName[lang] + '</h3>');
			}
			div.append(renderQuestions(section.questions));
		}
		return div;
	};

	renderQuestions = function (questions) {
		var i, question, subQuestions, div;
		div = $('<div>');
		div.addClass('questions');
		for (i = 0; (question = questions[i]); i++) {
			subQuestions = question.subQuestions ||													// multiple answer options
								[{ questionId: question.questionId }];								// single answer option
			// title row
			if (question.title && question.title[lang] !== "") {
				div.append($('<h4>').html(question.title[lang]));
			}
			// note
			if (question.note && question.note[lang] !== "") {
				div.append($('<small>').html(question.note[lang]));
			}
			// responses row
			div.append(
				$('<div>')
					.addClass('responses')
					.addClass(question.type)
					.addClass(question.subQuestions ? "multiple " + question.subQuestions[0].questionId : "single " + question.questionId)
					.append(
						typeof(responseItemRenderer[question.type]) === "function" ?
							responseItemRenderer[question.type](question, subQuestions) :
							'<div class="error">question type not supported</div>'
					)
			);
		}
		return div;
	};

	/* private ************************************************************************************/

	// generates a unique identifier using the Item's Id as prefix.
	id = function (/* args1, [arg2, ...] */) {
		var i = 0, arg, args = [];
		for (; (arg = arguments[i]); i++) {
			args.push(arg);
		}
		return itemId + '_' + args.join('_');
	};

	createTable = function (rowAndColData) {
		var i, j, rowData, colData, table, row, col;
		table = $('<table>');
		for (i = 0; (rowData = rowAndColData[i]); i++) {
			row = $('<tr>');
			for (j = 0; (colData = rowData[j]); j++) {
				col = $('<td>')
					.addClass('col' + j)
					.html(colData);
				row.append(col);
			}
			table.append(row);
		}
		return table;
	};

	/*createDateSelector = function (fieldId) {														// todo: prevent invalid manual values?
		var image, clickHandler;
		clickHandler = function (event) {
			var container = $('<div>'), currDate;
			event.preventDefault();			
			try {
				currDate = window.top.lipstick.parseDate("yy-mm-dd", $('#' + fieldId).val());					// try to parse the text value
			} catch (err) {
				currDate = new Date();
			}
			window.top.lipstick.createDatePicker(
				container,
				"yy-mm-dd",																			// CSTAR.current_user.date_format_pattern,
				window.top.CSTAR.current_user.date_format_symbols,
				{
					defaultDate: currDate,
					onSelect: function (dateText, inst) {											// todo use the user's date format (CSTAR.current_user.date_format)
						$('#' + fieldId).val(dateText).trigger('change');
						$.modal.close();
					}
				}
			);
			window.top.lipstick.showMethodDialog(container, {													// todo: dismiss the modal dialogue -> see table control
				title: 'Please select:',
				width: 244,
				showOk: false
			}, {});
		};
		image = $("<a>").attr('href', '#').addClass('icon calendar');
		if (!isLocked) {
			image.click(clickHandler);
		} else {
			image.addClass('disabled');
		}
		return image;
	};*/


	/* dev function *******************************************************************************/

	// dev function to set the questions! call via addressbar: javascript:void(setInitialData());
	setQuestionnaire = function (path) {
		var area, wrapper, key, predefinedKeys = [];
		path = path || "questionnaire";
		for (key in predefinedQuestionnaires) {
			if (predefinedQuestionnaires.hasOwnProperty(key)) {
				predefinedKeys.push(key);
			}
		}
		wrapper = $('<div/>').attr({id: id('sapui-admin')});
		wrapper.append("<h3>Please paste a new " + path + " structure (JSON) or type one of the predefined keys (" + predefinedKeys.join(', ') + "):</h3>");
		wrapper.append($("<textarea>").css('width', '100%').attr('id', id('nq-text')).val(JSON.stringify(initialData[path])));
		wrapper.append($('<input type="submit">').html('Submit').click(function () {
				var newQuestionnaire;
				newQuestionnaire = $('#' + id('nq-text')).val();
				if (predefinedQuestionnaires[newQuestionnaire]) {
					newQuestionnaire = predefinedQuestionnaires[newQuestionnaire];
				} else {
					try {
						newQuestionnaire = $.parseJSON(newQuestionnaire);
					} catch (err) {
						alert('Error parsing the ' + path + '. Please try again.');
						return;
					}
				}
				initialData.questionnaire = newQuestionnaire;
				osapi.appdata.update({userId: "@shared",data: {initdata: initialData}}).execute(function (userData) {
				    if (userData.error){
				      	alert(userData.error.message)
				    }
				    else{				      
						alert('The new Questionnaire has been set. Please reload the Activity.');
				    }
				});
			})
		);
		area = $('#'+itemId).prepend(wrapper);
		gadgets.window.adjustHeight();
		return false;
	};
	window.setQuestionnaire = setQuestionnaire;

	/* return handlers ****************************************************************************/

	handlers = {
		data_updated:	external_update,
		started:		render
	};
	return handlers;
};